package com.github.linkoog.model_designer.ide.editer;

import com.github.linkoog.model_designer.ide.MessageDialog;
import com.github.linkoog.model_designer.ide.editer.common.Column;
import com.github.linkoog.model_designer.ide.editer.common.ColumnVo;
import com.github.linkoog.model_designer.ide.editer.common.TableVo;
import com.github.linkoog.model_designer.ide.editer.dbmodel.DBModelConstants;
import com.github.linkoog.model_designer.utils.Assert;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.JBColor;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBLoadingPanel;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.YAMLElementGenerator;
import org.jetbrains.yaml.YAMLUtil;
import org.jetbrains.yaml.psi.*;

import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.util.Collection;
import java.util.List;

public class DBTableEditPanel implements Disposable, DumbAware {

    private final Project project;
    private final VirtualFile virtualFile;
    private final YAMLFile yamlFile;
    private final YAMLElementGenerator yamlElementGenerator;
    private final TableVo tableVo;
    private YAMLKeyValue tableKeyValue;


    private final JBLoadingPanel loadingPanel = new JBLoadingPanel(new BorderLayout(), this::dispose);
    private JPanel myMainPanel;
    private JPanel topPanel;
    private JPanel contextPanel;

    private JTextField nameTextField;
    private JTextArea commentTextArea;
    private JPanel columnsPanel;

    private JPanel tablePanel;
    private JPanel bottomPanel;
    private JScrollPane commentScrollPanel;

    private ColumnsTableBuilder columnsTableBuilder;

    public DBTableEditPanel(Project project, VirtualFile virtualFile, TableVo tableVo){
        loadingPanel.setLoadingText("init...");
        loadingPanel.startLoading();

        this.project = project;
        this.virtualFile = virtualFile;
        this.yamlFile = (YAMLFile)PsiManager.getInstance(project).findFile(virtualFile);
        this.yamlElementGenerator = YAMLElementGenerator.getInstance(project);
        this.tableVo = tableVo;
        this.tableKeyValue = getTableKeyValue();

        initPanelUI();
        resetPanelContext();
        loadingPanel.stopLoading();
    }

    private void initPanelUI(){
        loadingPanel.add(myMainPanel, BorderLayout.CENTER);

        //topPanel.setPreferredSize(new JBDimension(-1, 24, true));
        tablePanel.setBorder(new LineBorder(JBColor.LIGHT_GRAY));
        commentScrollPanel.setBorder(new LineBorder(JBColor.LIGHT_GRAY));
        bindPanelUIEvent();

        columnsTableBuilder = new ColumnsTableBuilder(project);

        ToolbarDecorator toolbar = columnsTableBuilder.getToolbarDecorator();
        ColumnsJTable columnsJTable = columnsTableBuilder.getColumnsJTable();
        ColumnsJTableModel columnsJTableModel = columnsJTable.getColumnsJTableModel();
        toolbar.setAddAction(anActionButton -> {
            loadingPanel.setLoadingText("Add a column to the table!");
            loadingPanel.startLoading();

            int selectedRow = columnsJTable.getSelectedRow();

            YAMLMapping tableMapping =  getTableMapping(DBTableEditPanel.this.tableKeyValue);
            YAMLMapping columnsMapping = getColumnsMapping(tableMapping);
            String indent = StringUtil.repeatSymbol(' ', YAMLUtil.getIndentInThisLine(columnsMapping));
            if (selectedRow == -1){
                int newIndex = (columnsJTableModel.getRowCount() + 1);
                ColumnVo column = ColumnVo.newStringColumnVo();
                column.setName("字段" + newIndex);


                final YAMLKeyValue copy = createKeyValue(indent, column);

                WriteCommandAction.runWriteCommandAction(project, () -> {
                    columnsMapping.putKeyValue(copy);
                    columnsJTableModel.addRow(column);
                    tableVo.getColumns().add(column);

                    columnsJTable.setRowSelectionInterval(newIndex, newIndex);

                    loadingPanel.stopLoading();
                });
                return;
            }

            Column column = columnsJTableModel.getRowData(selectedRow);
            ColumnVo columnVo = ColumnVo.copy(column.getColumnVo());
            columnVo.setName("字段" + (columnsJTableModel.getRowCount() + 1));
            columnVo.setPrimaryKey(false);
            columnVo.setComment("");

            YAMLKeyValue columnKeyValue = columnsMapping.getKeyValueByKey(column.getName());
            WriteCommandAction.runWriteCommandAction(project, () -> {
                int newIndex = (selectedRow + 1);
                YAMLKeyValue newKeyValue = createKeyValue(indent, columnVo);
                final PsiElement eol = columnsMapping.addAfter(yamlElementGenerator.createEol(), columnKeyValue);
                columnsMapping.addAfter(newKeyValue, eol);

                columnsJTableModel.addRow(newIndex, columnVo);
                tableVo.getColumns().add(newIndex, columnVo);

                columnsJTable.setRowSelectionInterval(newIndex, newIndex);
                loadingPanel.stopLoading();
            });
        });

        toolbar.setRemoveAction(anActionButton -> {
            int selectedRow = columnsJTable.getSelectedRow();
            if (selectedRow == -1){
                return;
            }

            loadingPanel.setLoadingText("Remove a column to the table!");
            loadingPanel.startLoading();

            Column column = columnsJTableModel.getRowData(selectedRow);
            YAMLMapping tableMapping =  getTableMapping(DBTableEditPanel.this.tableKeyValue);
            YAMLMapping columnsMapping = getColumnsMapping(tableMapping);
            YAMLKeyValue columnKeyValue = columnsMapping.getKeyValueByKey(column.getName());
            if (columnKeyValue != null){
                MessageDialog.builder()
                        .parent(myMainPanel)
                        .title("消息")
                        .message("是否删除该列？")
                        .callback(() -> {
                            WriteCommandAction.runWriteCommandAction(project, () -> {
                                columnsMapping.deleteKeyValue(columnKeyValue);
                                columnsJTableModel.removeRow(selectedRow);
                                tableVo.getColumns().remove(selectedRow);
                                loadingPanel.stopLoading();
                            });
                        }).build().show();
            }
        });

        toolbar.setMoveUpAction(anActionButton -> {
            int selectedRow = columnsJTable.getSelectedRow();
            if (selectedRow == -1 || selectedRow == 0){
                return;
            }

            loadingPanel.setLoadingText("Move Up a column for the table!");
            loadingPanel.startLoading();

            Column column = columnsJTableModel.getRowData(selectedRow);
            Column prevColumn = columnsJTableModel.getRowData(selectedRow - 1);
            Assert.isTrue(column != null && prevColumn != null, "column or prevColumn is null!");
            YAMLMapping tableMapping =  getTableMapping(DBTableEditPanel.this.tableKeyValue);
            YAMLMapping columnsMapping = getColumnsMapping(tableMapping);
            YAMLKeyValue columnKeyValue = columnsMapping.getKeyValueByKey(column.getName());
            YAMLKeyValue prevColumnKeyValue = columnsMapping.getKeyValueByKey(prevColumn.getName());
            Assert.isTrue(columnKeyValue != null && prevColumnKeyValue != null, "columnKeyValue or prevColumnKeyValue is null!");

            WriteCommandAction.runWriteCommandAction(project, () -> {
                final YAMLKeyValue copy = createKeyValue(prevColumnKeyValue.getText());

                columnsMapping.deleteKeyValue(prevColumnKeyValue);

                final PsiElement eol = columnsMapping.addAfter(yamlElementGenerator.createEol(), columnKeyValue);
                columnsMapping.addAfter(copy, eol);

                columnsJTableModel.moveUpRow(selectedRow);

                int newIndex = selectedRow - 1;
                List<Column> columns = tableVo.getColumns();
                Column col = columns.remove(selectedRow);
                columns.add(newIndex, col);

                columnsJTable.setRowSelectionInterval(newIndex, newIndex);
                loadingPanel.stopLoading();
            });
        });

        toolbar.setMoveDownAction(anActionButton -> {
            int selectedRow = columnsJTable.getSelectedRow();
            if (selectedRow == -1){
                return;
            }

            loadingPanel.setLoadingText("Move Down a column for the table!");
            loadingPanel.startLoading();

            Column column = columnsJTableModel.getRowData(selectedRow);
            Column nextColumn = columnsJTableModel.getRowData(selectedRow + 1);
            Assert.isTrue(column != null && nextColumn != null, "column or nextColumn is null!");
            YAMLMapping tableMapping =  getTableMapping(DBTableEditPanel.this.tableKeyValue);
            YAMLMapping columnsMapping = getColumnsMapping(tableMapping);
            YAMLKeyValue columnKeyValue = columnsMapping.getKeyValueByKey(column.getName());
            YAMLKeyValue nextColumnKeyValue = columnsMapping.getKeyValueByKey(nextColumn.getName());
            Assert.isTrue(columnKeyValue != null && nextColumnKeyValue != null, "columnKeyValue or nextColumnKeyValue is null!");

            WriteCommandAction.runWriteCommandAction(project, () -> {
                final YAMLKeyValue copy = createKeyValue(columnKeyValue.getText());

                columnsMapping.deleteKeyValue(columnKeyValue);

                final PsiElement eol = columnsMapping.addAfter(yamlElementGenerator.createEol(), nextColumnKeyValue);
                columnsMapping.addAfter(copy, eol);

                columnsJTableModel.moveDownRow(selectedRow);

                int newIndex = selectedRow + 1;
                List<Column> columns = tableVo.getColumns();
                Column col = columns.remove(selectedRow);
                columns.add(newIndex, col);

                columnsJTable.setRowSelectionInterval(newIndex, newIndex);
                loadingPanel.stopLoading();
            });
        });

        columnsJTableModel.setDataChangedListener((rowIndex, columnIndex, column, field, newVal) -> {
            loadingPanel.setLoadingText("edit a column for the table!");
            loadingPanel.startLoading();

            YAMLMapping tableMapping =  getTableMapping(DBTableEditPanel.this.tableKeyValue);
            YAMLMapping columnsMapping = getColumnsMapping(tableMapping);
            YAMLKeyValue columnKeyValue = columnsMapping.getKeyValueByKey(column.getName());
            Assert.isTrue(columnKeyValue != null, "columnKeyValue is null!");
            if (field.equals("name") ){
                WriteCommandAction.runWriteCommandAction(project, () -> {
                    YAMLUtil.rename(columnKeyValue, newVal);
                    loadingPanel.stopLoading();
                });
            } else {
                Assert.isTrue( columnKeyValue.getValue() != null && columnKeyValue.getValue() instanceof YAMLMapping, "columnKeyValue is error!");
                YAMLMapping columnMapping = (YAMLMapping)columnKeyValue.getValue();
                WriteCommandAction.runWriteCommandAction(project, () -> {
                    columnMapping.putKeyValue(yamlElementGenerator.createYamlKeyValue(field, newVal));
                    loadingPanel.stopLoading();
                });
            }
        });

        columnsPanel.add(columnsTableBuilder.getMyMainPanel(), BorderLayout.CENTER);

    }

    private YAMLKeyValue createKeyValue(String text) {
        final YAMLFile fileWithKey = yamlElementGenerator.createDummyYamlWithText(text);
        final YAMLMapping dummyMapping = PsiTreeUtil.findChildOfType(fileWithKey.getDocuments().get(0), YAMLMapping.class);
        assert dummyMapping != null && dummyMapping.getKeyValues().size() == 1;
        return dummyMapping.getKeyValues().iterator().next();
    }

    private YAMLKeyValue createKeyValue(String indent, ColumnVo columnVo){
        StringBuilder builder = new StringBuilder();
        builder.append(indent).append(columnVo.getName()).append(":");
        for (String field : ColumnVo.fields) {
            builder.append("\n").append(indent).append(StringUtil.repeatSymbol(' ', 2)).append(field).append(":").append(" ").append(columnVo.getField(field));
        }
        return createKeyValue(builder.toString());
    }

    private YAMLMapping getCommonColumnsMapping(){
        final YAMLDocument root = yamlFile.getDocuments().get(0);
        Assert.isTrue(root != null, "YAMLDocument is null!");
        YAMLMapping rootMapping = PsiTreeUtil.findChildOfType(root, YAMLMapping.class);
        Assert.isTrue(rootMapping != null, "rootMapping is null!");

        YAMLKeyValue comcolKeyValue = rootMapping.getKeyValueByKey(DBModelConstants.COMMON_COLUMNS);
        Assert.isTrue(comcolKeyValue != null && comcolKeyValue.getValue() != null && comcolKeyValue.getValue() instanceof YAMLMapping, "comcolKeyValue is error!");
        return (YAMLMapping) comcolKeyValue.getValue();
    }

    private YAMLKeyValue getTableKeyValue(){
        final YAMLDocument root = yamlFile.getDocuments().get(0);
        Assert.isTrue(root != null, "YAMLDocument is null!");
        YAMLMapping rootMapping = PsiTreeUtil.findChildOfType(root, YAMLMapping.class);
        Assert.isTrue(rootMapping != null, "rootMapping is null!");
        YAMLKeyValue tableKeyValue = rootMapping.getKeyValueByKey(tableVo.getName());
        Assert.isTrue(tableKeyValue != null && tableKeyValue.getValue() != null && tableKeyValue.getValue() instanceof YAMLMapping, "tableKeyValue is error!");
        return tableKeyValue;
    }

    private YAMLMapping getTableMapping(){
        return getTableMapping(getTableKeyValue());
    }

    private YAMLMapping getTableMapping(YAMLKeyValue tableKeyValue){
        Assert.isTrue(tableKeyValue != null && tableKeyValue.getValue() != null && tableKeyValue.getValue() instanceof YAMLMapping, "tableKeyValue is error!");
        return (YAMLMapping)tableKeyValue.getValue();
    }

    private YAMLMapping getColumnsMapping(){
        YAMLMapping tableMapping = getTableMapping();
        return getColumnsMapping(tableMapping);
    }

    private YAMLMapping getColumnsMapping(YAMLMapping tableMapping){
        Assert.isTrue(tableMapping != null, "tableMapping is null!");
        YAMLKeyValue columnsKeyValue = tableMapping.getKeyValueByKey("columns");
        Assert.isTrue(columnsKeyValue != null && columnsKeyValue.getValue() != null && columnsKeyValue.getValue() instanceof YAMLMapping, "columnsKeyValue is error!");
        return (YAMLMapping) columnsKeyValue.getValue();
    }




    private void bindPanelUIEvent(){
        nameTextField.getDocument().addDocumentListener(new DocumentAdapter() {

            @Override
            protected void textChanged(@NotNull DocumentEvent e) {
                String text = getDocumentText(e.getDocument());
                if (StringUtils.isBlank(text)){
                    return;
                }

                if (text.equals(tableVo.getName())){
                    //第一次初始化，会触发
                    return;
                }

                WriteCommandAction.runWriteCommandAction(project, () -> {
                    YAMLKeyValue tableKeyValue = DBTableEditPanel.this.tableKeyValue;
                    YAMLUtil.rename(tableKeyValue, text);
                    tableVo.setName(text);
                    DBTableEditPanel.this.tableKeyValue = getTableKeyValue();
                });
            }
        });

        nameTextField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
            }

            @Override
            public void focusLost(FocusEvent e) {
                final String text = nameTextField.getText();
                if (StringUtils.isBlank(text)){

                }
            }
        });

        commentTextArea.getDocument().addDocumentListener(new DocumentAdapter() {
            @Override
            protected void textChanged(@NotNull DocumentEvent e) {
                String text = getDocumentText(e.getDocument());
                if (text == null){
                    return;
                }

                if (text.equals(tableVo.getComment())){
                    return;
                }

                YAMLMapping tableMapping =  (YAMLMapping)tableKeyValue.getValue();
                YAMLKeyValue commentKeyValue =  tableMapping.getKeyValueByKey("comment");
                WriteCommandAction.runWriteCommandAction(project, () -> {
                    YAMLKeyValue newKeyValue = yamlElementGenerator.createYamlKeyValue("comment", text);
                    if (commentKeyValue == null){
                        tableMapping.add(newKeyValue);
                    } else {
                        YAMLValue newValue = newKeyValue.getValue();
                        final PsiElement value = commentKeyValue.getValue();
                        if (value == null) {
                            throw new IllegalStateException();
                        }
                        value.replace(newValue);
                        //tableMapping.putKeyValue(newKeyValue);
                    }
                    tableVo.setComment(text);
                });
            }
        });
    }

    private String getDocumentText(Document document){
        try {
            String text = document.getText(0, document.getLength());
            return text;
        } catch (BadLocationException ex) {
            ex.printStackTrace();
        }
        return null;
    }


    private void initYamlTableFileds(){
        final YAMLDocument root = yamlFile.getDocuments().get(0);
        assert root != null;

        final Application application = ApplicationManager.getApplication();
        application.invokeLater(() -> {WriteCommandAction.runWriteCommandAction(project, () -> {
            YAMLMapping rootMapping = PsiTreeUtil.findChildOfType(root, YAMLMapping.class);
            if (rootMapping == null) {
                final YAMLFile yamlFile = YAMLElementGenerator.getInstance(project).createDummyYamlWithText("name:\ncomment:\ncolumns:\n");
                final YAMLMapping mapping = (YAMLMapping) yamlFile.getDocuments().get(0).getTopLevelValue();
                assert mapping != null;
                rootMapping = ((YAMLMapping)root.add(mapping));
            }else {
                YAMLKeyValue nameKeyValue = rootMapping.getKeyValueByKey("name");
                if (nameKeyValue == null){
                    nameKeyValue = (YAMLKeyValue)rootMapping.addBefore(yamlElementGenerator.createYamlKeyValue("name", ""), rootMapping.getFirstChild());
                }

                YAMLKeyValue commentKeyValue = rootMapping.getKeyValueByKey("comment");
                if (commentKeyValue == null){
                    PsiElement element = rootMapping.addAfter(yamlElementGenerator.createEol(), nameKeyValue);
                    commentKeyValue = (YAMLKeyValue)rootMapping.addAfter(yamlElementGenerator.createYamlKeyValue("comment", ""), element);
                }

                YAMLKeyValue columnsKeyValue = rootMapping.getKeyValueByKey("columns");
                if (columnsKeyValue == null){
                    PsiElement element = rootMapping.addAfter(yamlElementGenerator.createEol(), commentKeyValue);
                    columnsKeyValue = (YAMLKeyValue)rootMapping.addAfter(yamlElementGenerator.createYamlKeyValue("columns", ""), element);
                }
            }
        });});
    }

    public void resetPanelContext() {
        if (tableVo == null){
            nameTextField.setText("");
            commentTextArea.setText("");
            resetColumnsTable(null);
        }else {
            nameTextField.setText(StringUtils.isNotBlank(tableVo.getName())? tableVo.getName() : "");
            commentTextArea.setText(StringUtils.isNotBlank(tableVo.getComment())? tableVo.getComment() : "");
            resetColumnsTable(tableVo.getColumns());
        }
    }

    private void resetColumnsTable(Collection<? extends Column> newRowList){
        columnsTableBuilder.getColumnsJTable().getColumnsJTableModel().reset(newRowList);
    }



    @Override
    public void dispose() {
    }

    public JPanel getMyMainPanel() {
        //return myMainPanel;
        return loadingPanel;
    }



}
